﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace AccessControl.Wpf.Controls
{
    /// <summary>
    /// Time Picker as a control that lets the user select a specific time
    /// </summary>
    public class TimePicker : Control
    {
        int HourMaxValue = 23;
        int MinuteMaxValue = 59;
        int SecondMaxValue = 59;
        int HourMinValue = 0;
        int MinuteMinValue = 0;
        int SecondMinValue = 0;

        bool _isChanging;
        TextBox _txtHours, _txtMinutes, _txtSeconds;

        private static readonly Type s_ThisType = typeof(TimePicker);

        #region Constructors

        /// <summary>
        /// Default constructor
        /// </summary>
        public TimePicker()
        {
            //SelectedTime = DateTime.Now.TimeOfDay;   
        }

        /// <summary>
        /// Static constructor
        /// </summary>
        static TimePicker()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TimePicker), new FrameworkPropertyMetadata(typeof(TimePicker)));
        }

        #endregion

        #region Dependency Properties

        public static readonly DependencyProperty SelectedTimeProperty = DependencyProperty.Register(
            "SelectedTime",
            typeof(TimeSpan),
            s_ThisType, 
            new PropertyMetadata(DateTime.Now.TimeOfDay, OnSelectedTimeChanged));

        #endregion

        #region Puvlic Properties

        public TimeSpan SelectedTime
        {
            get { return (TimeSpan)GetValue(SelectedTimeProperty); }
            set { SetValue(SelectedTimeProperty, value); }
        }

        #endregion

        #region Overrides

        /// <summary>
        /// override to hook to the Control template elements
        /// </summary>
        public override void OnApplyTemplate()
        {
            /* Get the hours text box  */
            _txtHours = GetTemplateChild("PART_Hours") as TextBox;            
            _txtHours.MouseWheel += (OnHoursMouseWheel);
            _txtHours.TextChanged += (OnHoursTextChanged);
            _txtHours.GotKeyboardFocus += (OnTextBoxGotKeyboardFocus);

            /* Get the minutes text box */
            _txtMinutes = GetTemplateChild("PART_Minutes") as TextBox;
            _txtMinutes.MouseWheel += (OnMinutesMouseWheel);
            _txtMinutes.TextChanged += (OnMinutesTextChanged);
            _txtMinutes.GotKeyboardFocus += (OnTextBoxGotKeyboardFocus);

            /* Get the seconds text box */
            _txtSeconds = GetTemplateChild("PART_Seconds") as TextBox;
            _txtSeconds.MouseWheel += (OnSecondsMouseWheel);
            _txtSeconds.TextChanged += (OnSecondsTextChanged);
            _txtSeconds.GotKeyboardFocus += (OnTextBoxGotKeyboardFocus);

            ApplyCurrentTimeToTextBoxes();
        }

        #endregion

        #region Private Members

        private static void OnSelectedTimeChanged(DependencyObject o,  DependencyPropertyChangedEventArgs e)
        {
            ((TimePicker)o).HandleSelectedTimeChanged();
        }

        private void HandleSelectedTimeChanged()
        {
            ApplyCurrentTimeToTextBoxes();
        }

        private void OnTextBoxGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            ((TextBox)sender).CaptureMouse();
        }

        private void OnHoursTextChanged(object sender, TextChangedEventArgs e)
        {
            int hours = SelectedTime.Hours;

            if (_isChanging)
                return;

            SetAndValidateNumber(_txtHours.Text, ref hours, HourMinValue, HourMaxValue);
            SelectedTime = new TimeSpan(hours, SelectedTime.Minutes, SelectedTime.Seconds);
        }

        private void OnHoursMouseWheel(object sender, MouseWheelEventArgs e)
        {
            int hours = SelectedTime.Hours;

            if (e.Delta > 0)
                IncrementValue(ref hours, HourMinValue, HourMaxValue);
            else if (e.Delta < 0)
                DecrementValue(ref hours, HourMinValue, HourMaxValue);

            SelectedTime = new TimeSpan(hours, SelectedTime.Minutes, SelectedTime.Seconds);
        }

        private void OnMinutesTextChanged(object sender, TextChangedEventArgs e)
        {
            int minutes = SelectedTime.Minutes;

            if (_isChanging)
                return;

            SetAndValidateNumber(_txtMinutes.Text, ref minutes, MinuteMinValue, MinuteMaxValue);
            SelectedTime = new TimeSpan(SelectedTime.Hours, minutes, SelectedTime.Seconds);
        }

        private void OnMinutesMouseWheel(object sender, MouseWheelEventArgs e)
        {
            int minutes = SelectedTime.Minutes;

            if (e.Delta > 0)
                IncrementValue(ref minutes, MinuteMinValue, MinuteMaxValue);
            else if (e.Delta < 0)
                DecrementValue(ref minutes, MinuteMinValue, MinuteMaxValue);

            SelectedTime = new TimeSpan(SelectedTime.Hours, minutes, SelectedTime.Seconds);
        }

        private void OnSecondsTextChanged(object sender, TextChangedEventArgs e)
        {
            int seconds = SelectedTime.Seconds;

            if (_isChanging)
                return;

            SetAndValidateNumber(_txtSeconds.Text, ref seconds, SecondMinValue, SecondMaxValue);
            SelectedTime = new TimeSpan(SelectedTime.Hours, SelectedTime.Minutes, seconds);
        }

        private void OnSecondsMouseWheel(object sender, MouseWheelEventArgs e)
        {
            int seconds = SelectedTime.Seconds;

            if (e.Delta > 0)
                IncrementValue(ref seconds, SecondMinValue, SecondMaxValue);
            else if (e.Delta < 0)
                DecrementValue(ref seconds, SecondMinValue, SecondMaxValue);

            SelectedTime = new TimeSpan(SelectedTime.Hours, SelectedTime.Minutes, seconds);
        }
        

        private void SetAndValidateNumber(string text, ref int value, int MinValue, int MaxValue)
        {
            if (string.IsNullOrEmpty(text) || !int.TryParse(text, out value))
                value = 0;

            if (value < MinValue)
                value = MinValue;
            if (value > MaxValue)
                value = MaxValue;
        }

        private void IncrementValue(ref int value, int MinValue, int MaxValue)
        {
            if ((value + 1) < MaxValue)
                value += 1;
            else
                value = MinValue;
        }

        private void DecrementValue(ref int value, int MinValue, int MaxValue)
        {
            if ((value - 1) > MinValue)
                value -= 1;
            else
                value = MaxValue;
        }

        private void ApplyCurrentTimeToTextBoxes()
        {
            _isChanging = true;

            if (_txtHours != null)
                _txtHours.Text = SelectedTime.Hours.ToString();
            if (_txtMinutes != null)
                _txtMinutes.Text = SelectedTime.Minutes.ToString();
            if (_txtSeconds != null)
                _txtSeconds.Text = SelectedTime.Seconds.ToString();
            
            _isChanging = false;
        }

        #endregion

    }
}